#region
/* Adoor.NET - Object/Relational mapping framework leveraging ADO.NET`s powerful disconnected mode.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *  
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */ 
#endregion

using System.Diagnostics;
using Adoor.Object.Domain;
using Adoor.Object.Query;
using Adoor.Relational;

namespace Adoor.Object.Relational
{
	// When preloading, we only want the proper instances of a particular entity, not all its instances
	public class PreloadQueryGenerator: RelationalQueryGenerator
	{
		protected string correlation; // when correlation != "" & mappingStrategy != null, replace each occurrence of correlation's query with actualFactory's query
		protected EntityData entityData;

		public static RelationalOperator GenerateOperator(MappingStrategyEnum mappingKind, ObjectPath path, string correlation, EntityData entityData)
		{
			PreloadQueryGenerator e = new PreloadQueryGenerator(mappingKind, correlation, entityData);
			path.EvaluateFrom(e);
			
            return e.result;
		}

		public PreloadQueryGenerator(MappingStrategyEnum mappingKind, string correlation, EntityData entityData)
			: base(mappingKind)
		{
			this.correlation = correlation;
			this.entityData = entityData;
		}

		public override void EvaluateScan(ScanPath a)
		{
			// when we scan the preloaded type, we only want its proper instances
			if (a.Alias == this.correlation)
			{
				IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)this.entityData.GetService(typeof(IEntityMappingStrategyProvider));
				IEntityMappingStrategy mappingStrategy = provider.GetMappingStrategy(mappingKind);
				this.result = mappingStrategy.ProperInstancesQuery.Rename(a.Alias);
			}
			else
			{
				IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)a.EntityData.GetService(typeof(IEntityMappingStrategyProvider));
				IEntityMappingStrategy mappingStrategy = provider.GetMappingStrategy(mappingKind);
				this.result = mappingStrategy.AllInstancesQuery.Rename(a.Alias);
			}
		}

		public override void EvaluateJoin(JoinPath j)
		{
			// when the preloaded type is the target of a join, we only want its proper instances
			j.InnerQuery.EvaluateFrom(this);
			IEntityMappingStrategy targetMappingStrategy;
			RelationalQuery targetQuery;
			if (j.Alias == this.correlation)
			{
				IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)this.entityData.GetService(typeof(IEntityMappingStrategyProvider));
				targetMappingStrategy = provider.GetMappingStrategy(mappingKind);
				targetQuery = targetMappingStrategy.ProperInstancesQuery;
			}
			else 
			{
				IEntityMappingStrategyProvider provider = (IEntityMappingStrategyProvider)j.ReferenceData.TargetData.GetService(typeof(IEntityMappingStrategyProvider));
				targetMappingStrategy = provider.GetMappingStrategy(this.mappingKind);
				targetQuery = targetMappingStrategy.AllInstancesQuery;
			}
			IKeyMappingStrategy targetKey = targetMappingStrategy.KeyMappingStrategy;

			
			// when we preload a child entity, the reference should be that of the child, not that of the ancestor
			ReferenceData referenceData;
			if (j.Correlation.Name == this.correlation)
			{
				referenceData = this.entityData.ReferenceInfos[j.Role];
			}
			else 
			{
				referenceData = j.ReferenceData;
			}

			IReferenceMappingStrategyProvider referenceProvider = (IReferenceMappingStrategyProvider)referenceData.GetService(typeof(IReferenceMappingStrategyProvider));
			this.result = referenceProvider.GetMappingStrategy(this.mappingKind).Join(j.IsOuterJoin, this.result, targetQuery, targetKey, j.Correlation.Name, j.Alias, j.Partial, j.Correlation.EntityData);
		}
	}
}